/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		objcontainertypes.cpp
 * PURPOSE:		base class of container object types
 */

namespace ObjectTypes
{
	struct ContainerObject;

	namespace InhSource
	{
		void GetTreeEditState(OUT SD_EDIT_STATE &Es, ContainerObject *Start, GENERIC_MAPPING Gm, UCHAR Privileges);
	}//namespace InhSource

}//namespace ObjectTypes

namespace ObjectTypes
{

struct ContainerObject : virtual public Object
{
	GENERIC_MAPPING GenericMapping;

	ContainerObject(GENERIC_MAPPING GenericMapping) :
		GenericMapping(GenericMapping)
	{}

	virtual void QueryTreeEditState(OUT SD_EDIT_STATE &Es, UCHAR Privileges) override
	{
		InhSource::GetTreeEditState(Es, this, GenericMapping, Privileges);
	}

	virtual void SetTreeEditState(IN SD_EDIT_STATE const &Es, UCHAR Privileges) override
	{
		SdMerge::MergeTreeEditState(Es, *this, Privileges);
	}

	virtual GENERIC_MAPPING GetGenericMapping() override
	{
		return GenericMapping;
	}
};

namespace InhSource
{

USHORT CountAcesNeedingSource(IN ACL const *Acl)
{
	if(!Acl)
		return 0;

	USHORT Ct = 0;

	union
	{
		ACE *Ace;
		ULONG_PTR Pos;
	};

	Pos = reinterpret_cast<ULONG_PTR>(Acl) + sizeof(ACL);

	for(USHORT i = 0; i < Acl->AceCount; i++)
	{
		if(!(Ace->Flags & ACE_FLAG_INH_SOURCE_KNOWN) && Ace->Flags & ACE_FLAG_INHERITED)
			Ct++;

		Pos += Ace->Size;
	}

	return Ct;
}

//returns number of source levels set
USHORT SetAceSourcesAtLevel(OUT ACL *Dest, IN ACL const *Level, BOOLEAN DestIsContainer, USHORT LevelNum, GENERIC_MAPPING Gm)
{
	if(!Dest || !Level)
		return 0;

	union
	{
		ACE *DestAce;
		ULONG_PTR DestPos;
	};

	DestPos = reinterpret_cast<ULONG_PTR>(Dest) + sizeof(ACL);

	union
	{
		ACE *LevelAce;
		ULONG_PTR LevelPos;
	};

	USHORT RequiredBits = DestIsContainer ? ACE_FLAG_CONTAINER_INHERIT : ACE_FLAG_OBJECT_INHERIT;
	USHORT SetCt = 0;

	for(USHORT i = 0; i < Dest->AceCount; i++)
	{
		if(!(DestAce->Flags & ACE_FLAG_INH_SOURCE_KNOWN) && DestAce->Flags & ACE_FLAG_INHERITED)
		{
			LevelPos = reinterpret_cast<ULONG_PTR>(Level) + sizeof(ACL);

			for(USHORT j = 0; j < Level->AceCount; j++)
			{
				if(LevelAce->Flags & RequiredBits && AreAcesEffectivelyEqual(DestAce, LevelAce, Gm))
				{
					if(LevelAce->Flags & ACE_FLAG_INHERITED)
						goto next_destace;
					else
					{
						DestAce->Source = LevelNum;
						DestAce->Flags |= ACE_FLAG_INH_SOURCE_KNOWN;
						SetCt++;
						goto next_destace;
					}
				}

				LevelPos += LevelAce->Size;
			}
			//no ACEs could be found that could be the inheritance source of this ACE (either directly or indirectly)
			DestAce->Flags |= ACE_FLAG_INH_SOURCE_KNOWN | ACE_FLAG_INH_INVALID;
		}

next_destace:;

		DestPos += DestAce->Size;
	}

	return SetCt;
}

void SetNearAceSources(OUT ACL *Dest, USHORT LevelNum)
{
	if(!Dest)
		return;

	union
	{
		ACE *DestAce;
		ULONG_PTR DestPos;
	};

	DestPos = reinterpret_cast<ULONG_PTR>(Dest) + sizeof(ACL);

	for(USHORT i = 0; i < Dest->AceCount; i++)
	{
		if(!(DestAce->Flags & ACE_FLAG_INH_SOURCE_KNOWN) && DestAce->Flags & ACE_FLAG_INHERITED)
		{
			DestAce->Source = LevelNum;
			DestAce->Flags |= ACE_FLAG_INH_SOURCE_KNOWN | ACE_FLAG_INH_SOURCE_MINONLY;
		}

		DestPos += DestAce->Size;
	}
}

void SetInvalidAceSources(OUT ACL *Dest)
{
	if(!Dest)
		return;

	union
	{
		ACE *DestAce;
		ULONG_PTR DestPos;
	};

	DestPos = reinterpret_cast<ULONG_PTR>(Dest) + sizeof(ACL);

	for(USHORT i = 0; i < Dest->AceCount; i++)
	{
		if(!(DestAce->Flags & ACE_FLAG_INH_SOURCE_KNOWN) && DestAce->Flags & ACE_FLAG_INHERITED)
		{
			DestAce->Flags |= ACE_FLAG_INH_SOURCE_KNOWN | ACE_FLAG_INH_INVALID;
			DestAce->Source++;
		}

		DestPos += DestAce->Size;
	}
}

Object *OpenParentSEH(Object *Obj, UCHAR Privileges)
{
	__try
	{
		return Obj->OpenParent(Privileges);
	}
	__except(IsExpectedException(exception_code()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		return NULL;
	}
}

void GetTreeEditState(OUT SD_EDIT_STATE &Es, ContainerObject *Start, GENERIC_MAPPING Gm, UCHAR Privileges)
{
	Start->QueryEditState(Es);
	auto_refct<Object> Obj;

	USHORT DaclRemaining = CountAcesNeedingSource(Es.Dacl.Acl);
	USHORT SaclRemaining = CountAcesNeedingSource(Es.Sacl.Acl);
	USHORT LevelNum = 1;

	Es.Dacl.State |= ACL_FLAG_AUTO_INH_SOURCES;
	Es.Sacl.State |= ACL_FLAG_AUTO_INH_SOURCES;

	while(DaclRemaining || SaclRemaining)
	{
		OpText Ot(L"Finding the source of inherited ACEs", Obj ? Obj->Name.get() : Start->Name.get());

		Obj = OpenParentSEH(Obj ? Obj.get() : Start, Privileges);//continue with the current object if it exists or start at the top (Obj is never = Start to avoid freeing Start)

		if(!Obj) //there was an error, so fill all in as nearest
		{
			SetNearAceSources(Es.Dacl.Acl, LevelNum);
			SetNearAceSources(Es.Sacl.Acl, LevelNum);
			break;
		}

		SD_EDIT_STATE Level;

		Obj->QueryEditState((DaclRemaining ? DACL_SECURITY_INFORMATION : 0) | (SaclRemaining ? SACL_SECURITY_INFORMATION : 0), Level);

		if(DaclRemaining)
			DaclRemaining = DaclRemaining - SetAceSourcesAtLevel(Es.Dacl.Acl, Level.Dacl.Acl, (Es.State & SD_FLAG_CONTAINER) != 0, LevelNum, Start->GenericMapping);

		if(SaclRemaining)
			SaclRemaining = SaclRemaining - SetAceSourcesAtLevel(Es.Sacl.Acl, Level.Sacl.Acl, (Es.State & SD_FLAG_CONTAINER) != 0, LevelNum, Start->GenericMapping);

		LevelNum++;

		//if this ACL was protected, there can be no deeper properly inherited ACEs
		if(Level.Dacl.State & ACL_FLAG_PROTECTED)
		{
			DaclRemaining = 0;
			SetInvalidAceSources(Es.Dacl.Acl);
		}

		if(Level.Sacl.State & ACL_FLAG_PROTECTED)
		{
			SaclRemaining = 0;
			SetInvalidAceSources(Es.Sacl.Acl);
		}

		Ot;
	}
}

}//namespace InhSource

}//namespace ObjectTypes